package com.elight.utils;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.beanutils.BeanUtils;

/**
 * Map工具类
 * @author zhoujunwen
 * @date 2015-06-26
 */
public class MapUtils {

	/** default separator between key and value **/
	public static final String DEFAULT_KEY_AND_VALUE_SEPARATOR = ":";
	/** default separator between key-value pairs **/
	public static final String DEFAULT_KEY_AND_VALUE_PAIR_SEPARATOR = ",";

	private MapUtils() {
		throw new AssertionError();
	}

	/**
	 * 判断map是否为空
	 */
	public static <K, V> boolean isEmpty(Map<K, V> sourceMap) {
		return (sourceMap == null || sourceMap.size() == 0);
	}

	/**
	 * map中添加一个不能为空的值 value为空不添加
	 */
	public static boolean putMapNotEmptyKey(Map<String, String> map,
			String key, String value) {
		if (map == null || StringUtils.isEmpty(key)) {
			return false;
		}
		map.put(key, value);
		return true;
	}

	/**
	 * map中添加一个不能为空的键值对
	 */
	public static boolean putMapNotEmptyKeyAndValue(Map<String, String> map,
			String key, String value) {
		if (map == null || StringUtils.isEmpty(key)
				|| StringUtils.isEmpty(value)) {
			return false;
		}
		map.put(key, value);
		return true;
	}

	/**
	 * map中添加一个不为空的键值对,若为空使用默认值
	 */
	public static boolean putMapNotEmptyKeyAndValue(Map<String, String> map,
			String key, String value, String defaultValue) {
		if (map == null || StringUtils.isEmpty(key)) {
			return false;
		}

		map.put(key, StringUtils.isEmpty(value) ? defaultValue : value);
		return true;
	}

	/**
	 * add key-value pair to map, key need not null
	 * 
	 * @param map
	 * @param key
	 * @param value
	 * @return <ul>
	 *         <li>if map is null, return false</li>
	 *         <li>if key is null, return false</li>
	 *         <li>return {@link Map#put(Object, Object)}</li>
	 *         </ul>
	 */
	public static <K, V> boolean putMapNotNullKey(Map<K, V> map, K key, V value) {
		if (map == null || key == null) {
			return false;
		}

		map.put(key, value);
		return true;
	}

	/**
	 * add key-value pair to map, both key and value need not null
	 * 
	 * @param map
	 * @param key
	 * @param value
	 * @return <ul>
	 *         <li>if map is null, return false</li>
	 *         <li>if key is null, return false</li>
	 *         <li>if value is null, return false</li>
	 *         <li>return {@link Map#put(Object, Object)}</li>
	 *         </ul>
	 */
	public static <K, V> boolean putMapNotNullKeyAndValue(Map<K, V> map, K key,
			V value) {
		if (map == null || key == null || value == null) {
			return false;
		}

		map.put(key, value);
		return true;
	}

	/**
	 * get key by value, match the first entry front to back
	 * <ul>
	 * <strong>Attentions:</strong>
	 * <li>for HashMap, the order of entry not same to put order, so you may
	 * need to use TreeMap</li>
	 * </ul>
	 * 
	 * @param <V>
	 * @param map
	 * @param value
	 * @return <ul>
	 *         <li>if map is null, return null</li>
	 *         <li>if value exist, return key</li>
	 *         <li>return null</li>
	 *         </ul>
	 */
	public static <K, V> K getKeyByValue(Map<K, V> map, V value) {
		if (isEmpty(map)) {
			return null;
		}

		for (Entry<K, V> entry : map.entrySet()) {
			if (ObjectUtils.isEquals(entry.getValue(), value)) {
				return entry.getKey();
			}
		}
		return null;
	}

	/**
	 * parse key-value pairs to map, ignore empty key
	 * 
	 * <pre>
	 * parseKeyAndValueToMap("","","",true)=null
	 * parseKeyAndValueToMap(null,"","",true)=null
	 * parseKeyAndValueToMap("a:b,:","","",true)={(a,b)}
	 * parseKeyAndValueToMap("a:b,:d","","",true)={(a,b)}
	 * parseKeyAndValueToMap("a:b,c:d","","",true)={(a,b),(c,d)}
	 * parseKeyAndValueToMap("a=b, c = d","=",",",true)={(a,b),(c,d)}
	 * parseKeyAndValueToMap("a=b, c = d","=",",",false)={(a, b),( c , d)}
	 * parseKeyAndValueToMap("a=b, c=d","=", ",", false)={(a,b),( c,d)}
	 * parseKeyAndValueToMap("a=b; c=d","=", ";", false)={(a,b),( c,d)}
	 * parseKeyAndValueToMap("a=b, c=d", ",", ";", false)={(a=b, c=d)}
	 * </pre>
	 * 
	 * @param source
	 *            key-value pairs
	 * @param keyAndValueSeparator
	 *            separator between key and value
	 * @param keyAndValuePairSeparator
	 *            separator between key-value pairs
	 * @param ignoreSpace
	 *            whether ignore space at the begging or end of key and value
	 * @return
	 */
	public static Map<String, String> parseKeyAndValueToMap(String source,
			String keyAndValueSeparator, String keyAndValuePairSeparator,
			boolean ignoreSpace) {
		if (StringUtils.isEmpty(source)) {
			return null;
		}

		if (StringUtils.isEmpty(keyAndValueSeparator)) {
			keyAndValueSeparator = DEFAULT_KEY_AND_VALUE_SEPARATOR;
		}
		if (StringUtils.isEmpty(keyAndValuePairSeparator)) {
			keyAndValuePairSeparator = DEFAULT_KEY_AND_VALUE_PAIR_SEPARATOR;
		}
		Map<String, String> keyAndValueMap = new HashMap<String, String>();
		String[] keyAndValueArray = source.split(keyAndValuePairSeparator);
		if (keyAndValueArray == null) {
			return null;
		}

		int seperator;
		for (String valueEntity : keyAndValueArray) {
			if (!StringUtils.isEmpty(valueEntity)) {
				seperator = valueEntity.indexOf(keyAndValueSeparator);
				if (seperator != -1) {
					if (ignoreSpace) {
						MapUtils.putMapNotEmptyKey(keyAndValueMap, valueEntity
								.substring(0, seperator).trim(), valueEntity
								.substring(seperator + 1).trim());
					} else {
						MapUtils.putMapNotEmptyKey(keyAndValueMap,
								valueEntity.substring(0, seperator),
								valueEntity.substring(seperator + 1));
					}
				}
			}
		}
		return keyAndValueMap;
	}

	/**
	 * parse key-value pairs to map, ignore empty key
	 * 
	 * @param source
	 *            key-value pairs
	 * @param ignoreSpace
	 *            whether ignore space at the begging or end of key and value
	 * @return
	 * @see {@link MapUtils#parseKeyAndValueToMap(String, String, String, boolean)}
	 *      , keyAndValueSeparator is {@link #DEFAULT_KEY_AND_VALUE_SEPARATOR},
	 *      keyAndValuePairSeparator is
	 *      {@link #DEFAULT_KEY_AND_VALUE_PAIR_SEPARATOR}
	 */
	public static Map<String, String> parseKeyAndValueToMap(String source,
			boolean ignoreSpace) {
		return parseKeyAndValueToMap(source, DEFAULT_KEY_AND_VALUE_SEPARATOR,
				DEFAULT_KEY_AND_VALUE_PAIR_SEPARATOR, ignoreSpace);
	}

	/**
	 * parse key-value pairs to map, ignore empty key, ignore space at the
	 * begging or end of key and value
	 * 
	 * @param source
	 *            key-value pairs
	 * @return
	 * @see {@link MapUtils#parseKeyAndValueToMap(String, String, String, boolean)}
	 *      , keyAndValueSeparator is {@link #DEFAULT_KEY_AND_VALUE_SEPARATOR},
	 *      keyAndValuePairSeparator is
	 *      {@link #DEFAULT_KEY_AND_VALUE_PAIR_SEPARATOR}, ignoreSpace is true
	 */
	public static Map<String, String> parseKeyAndValueToMap(String source) {
		return parseKeyAndValueToMap(source, DEFAULT_KEY_AND_VALUE_SEPARATOR,
				DEFAULT_KEY_AND_VALUE_PAIR_SEPARATOR, true);
	}
	
	
	/**
	 * map转成实体对象
	 * @param map
	 * @param clazz
	 * @return
	 */
	public static <T>T mapToBean(Map<String, Object> map,Class<T> clazz){
		T t=null;
		try {
			t = clazz.newInstance();
			if(isEmpty(map)){
				return t;
			}
			BeanUtils.populate(t, map);  
		} catch (InstantiationException e) {
			System.out.println("实例化对象失败：" + e);
		} catch (IllegalAccessException e) {
			System.out.println("实例化对象失败：" + e);
		} catch (InvocationTargetException e) {
			System.out.println("转换成Bean失败：" + e);
		}
		return t;
	}
	
	
	// Bean --> Map 1: 利用Introspector和PropertyDescriptor 将Bean --> Map  
    public static Map<String, Object> bean2Map(Object obj) {  
        if(obj == null){  
            return null;  
        }          
        Map<String, Object> map = new HashMap<String, Object>();  
        try {  
            BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());  
            PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();  
            for (PropertyDescriptor property : propertyDescriptors) {  
                String key = property.getName();  
                // 过滤class属性  
                if (!key.equals("class")) {  
                    // 得到property对应的getter方法  
                    Method getter = property.getReadMethod();  
                    Object value = getter.invoke(obj);  
                    map.put(key, value);  
                }  
  
            }  
        } catch (Exception e) {  
            System.out.println("对象转map失败：" + e);  
        }  
  
        return map;  
  
    }  
	

	/**
	 * 将map转换成JSON字符串
	 * 
	 * @param map
	 * @return
	 */
	public static String toJson(Map<String, String> map) {
		if (map == null || map.size() == 0) {
			return null;
		}
		StringBuilder paras = new StringBuilder();
		paras.append("{");
		Iterator<Entry<String, String>> ite = map.entrySet().iterator();
		while (ite.hasNext()) {
			Entry<String, String> entry = (Entry<String, String>) ite.next();
			paras.append("\"").append(entry.getKey()).append("\":\"").append(entry.getValue()).append("\"");
			if (ite.hasNext()) {
				paras.append(",");
			}
		}
		paras.append("}");
		return paras.toString();
	}
}
